package at.jku.cp.mir;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

import com.google.common.collect.Multiset;
import com.google.common.collect.TreeMultiset;
import com.google.common.collect.Multiset.Entry;

public class TagExtractor
{

	public static void main(String[] args) throws JDOMException, IOException
	{
		TagExtractor tagExtractor = new TagExtractor();
		tagExtractor.extractTagsFromDir("artistTags");
	}

	private SAXBuilder builder;
	private XPath tagXPath;
	private XPath artistXPath;

	public TagExtractor() throws JDOMException
	{
		super();

		builder = new SAXBuilder();
		tagXPath = XPath.newInstance("//tag");
		artistXPath = XPath.newInstance("//toptags/@artist");
	}

	public void extractTagsFromDir(String directory) throws JDOMException, IOException
	{
		File dir = new File(directory);
		File[] allFiles = dir.listFiles();

		// this map holds all artists and their tag-multisets
		Map<String, Multiset<String>> artistTags = new TreeMap<String, Multiset<String>>();

		// this multiset holds all tags and their occurences
		TreeMultiset<String> tagSet = TreeMultiset.create();

		// This map contains the sum of all tags for a given artist
		Map<String, Integer> artistTagCount = new TreeMap<String, Integer>();

		for (File tagFile : allFiles)
		{
			System.out.println(tagFile.getName());

			Document tagDoc = builder.build(tagFile);
			String artist = ((Attribute) artistXPath.selectSingleNode(tagDoc)).getValue();

			List<Element> tagNodes = tagXPath.selectNodes(tagDoc);
			System.out.println("#tags: " + tagNodes.size());

			TreeMultiset<String> artistTagSet = TreeMultiset.create();

			int i = 0;
			int nrOfTags = 0;
			for (Element tagNode : tagNodes)
			{
				String tagName = tagNode.getChildText("name");
				String tagCountString = tagNode.getChildText("count");
				int count = Integer.parseInt(tagCountString);

				// only add tag if its count is greater than 0
				if (count > 0)
				{
					tagSet.add(tagName, 1);
					artistTagSet.add(tagName, count);
				}

				// System.out.println(tagName + ": " + count);

				// continue with next artist if we have already reached more
				// than 10 tags

				nrOfTags += count;
			}

			artistTagCount.put(artist, Integer.valueOf(nrOfTags));
			artistTags.put(artist, artistTagSet);
			System.out.println("----------");
		}

		// get the number of differents tags
		int size = tagSet.size();
		System.out.println("number of tags: " + size);

		// order multiset by occurences
		List<Entry<String>> sortedTagList = sortMultisetPerEntryCount(tagSet);
		Entry<String> firstTagEntry = sortedTagList.get(0);
		String firstTag = firstTagEntry.getElement();
		Set<String> artists = artistTags.keySet();
		System.out.println("-----");
		System.out.println("-----");
		System.out.println("Listing all tag occurences for tag: " + firstTag);
		for (String artist : artists)
		{
			int count = artistTags.get(artist).count(firstTag);
			double nrOfTagsForArtist = artistTagCount.get(artist).doubleValue();
			double tf = count / nrOfTagsForArtist;

			int D = artists.size();
			double nrOfArtistsWithThisTag = tagSet.count(firstTag);
			double idf = Math.log(D / (1 + nrOfArtistsWithThisTag));

			double tfidf = tf * idf;

			System.out.println(artist + ": " + count + " --> tf=" + tf + "       idf=" + idf + "  tfidf=" + tfidf);
		}

	}

	/**
	 * from
	 * http://philippeadjiman.com/blog/2010/02/20/a-generic-method-for-sorting
	 * -google-collections-multiset-per-entry-count/
	 * @param <T>
	 * @param multiset
	 * @return
	 */
	public <T> List<Entry<T>> sortMultisetPerEntryCount(Multiset<T> multiset)
	{
		Comparator<Multiset.Entry<T>> occurence_comparator = new Comparator<Multiset.Entry<T>>()
		{
			public int compare(Multiset.Entry<T> e1, Multiset.Entry<T> e2)
			{
				return e2.getCount() - e1.getCount();
			}
		};
		List<Entry<T>> sortedByCount = new ArrayList<Entry<T>>(multiset.entrySet());
		Collections.sort(sortedByCount, occurence_comparator);

		return sortedByCount;
	}

}
